package labox.innovation.gameserver.model.quest;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javolution.util.FastList;
import javolution.util.FastMap;
import labox.innovation.config.Config;
import labox.innovation.db.core.L2DatabaseFactory;
import labox.innovation.gameserver.cache.HtmCache;
import labox.innovation.gameserver.datatables.NpcTemplateTable;
import labox.innovation.gameserver.instancemanager.QuestManager; //import labox.innovation.gameserver.instancemanager.ZoneManager;
import labox.innovation.gameserver.model.FuzeWorld;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Object;
import labox.innovation.gameserver.model.L2Party;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.L2Spawn;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.L2Npc;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance.PlayerQuestValues;
import labox.innovation.gameserver.model.quest.dataset.ILogicTemplet;
import labox.innovation.gameserver.model.quest.dataset.QuestDataSet;
import labox.innovation.gameserver.model.zone.L2ZoneType;
import labox.innovation.gameserver.network.serverpackets.ActionFailed;
import labox.innovation.gameserver.network.serverpackets.NpcHtmlMessage;
import labox.innovation.gameserver.scripting.ManagedScript;
import labox.innovation.gameserver.scripting.ScriptManager;
import labox.innovation.gameserver.templates.chars.NpcTemplate;
import labox.innovation.gameserver.timer.Timer;
import labox.innovation.util.Rnd;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Luis Arias
 * 
 */
public class Quest extends ManagedScript {

	protected static final Logger _log = LoggerFactory.getLogger(Quest.class.getName());

	/** HashMap containing events from String value of the event */
	private static Map<String, Quest> _allEventsS = new FastMap<String, Quest>();

	/** HashMap containing lists of timers from the name of the timer */
	private Map<String, FastList<QuestTimer>> _allEventTimers = new FastMap<String, FastList<QuestTimer>>();

	private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();

	/**
	 * 任务Id
	 */
	private final int _questId;

	/**
	 * 英文名称(应用内部使用)
	 */
	private final String _name;

	/**
	 * 任务名称(模板填充)
	 */
	private final String _descr;

	/**
	 * 任务模板数据集
	 */
	private QuestDataSet _dataSet;

	private final byte _initialState = State.CREATED;

	private boolean _onEnterWorld = false;

	public static final String delimiter = "`~`";

	// NOTE: questItemIds will be overridden by child classes.  Ideally, it should be
	// protected instead of public.  However, quest scripts written in Jython will
	// have trouble with protected, as Jython only knows private and public...
	// In fact, protected will typically be considered private thus breaking the scripts.
	// Leave this as public as a workaround.
	public int[] questItemIds = null;// 用于注册任务物品,在调用QuestState.exitQuest用于清除任务物品

	/**
	 * Return collection view of the values contains in the allEventS
	 * 
	 * @return Collection<Quest>
	 */
	public static Collection<Quest> findAllEvents() {
		return _allEventsS.values();
	}

	/**
	 * (Constructor)Add values to class variables and put the quest in HashMaps. <br>
	 * 父类ManagedScript,构造方法将保存scriptFile,lastLoadTime<br>
	 * _scriptFile 文件内容<br>
	 * _lastLoadTime 记录最后文件加载时间<br>
	 * 
	 * @param questId
	 *            : 任务id (int)
	 * @param name
	 *            : 任务的名称(String):id_任务类型
	 * @param descr
	 *            : 任务描述(String)
	 */
	public Quest(int questId, String name, String descr) {
		_questId = questId;
		_name = name;
		_descr = descr;

		if (questId != 0) {
			QuestManager.getInstance().addQuest(Quest.this);
		} else {
			_allEventsS.put(name, this);
		}
		init_LoadGlobalData();
	}

	/**
	 * 填充数据集
	 * 
	 * @param dataSet
	 *            2010-4-21
	 * @author lyh
	 */
	public void putQuestDataSet(QuestDataSet dataSet) {
		if (dataSet == null)
			return;
		_dataSet = dataSet;
	}

	/**
	 * 返回任务数据集
	 * 
	 * @return 2010-4-21
	 * @author lyh
	 */
	public QuestDataSet getQuestDataSet() {
		return this._dataSet;
	}

	/**
	 * The function init_LoadGlobalData is, by default, called by the constructor of all quests. Children of this class can implement this function in order to define what variables to load and what structures to save them in. By default, nothing is loaded.
	 */
	protected void init_LoadGlobalData() {

	}

	/**
	 * The function saveGlobalData is, by default, called at shutdown, for all quests, by the QuestManager. Children of this class can implement this function in order to convert their structures into <var, value> tuples and make calls to save them to the database, if needed. By default, nothing is saved.
	 */
	public void saveGlobalData() {

	}

	public static enum QuestEventType {
		/**
		 * npc操作的参项第一个对话框显示，点击（一些任务必须覆盖默认的npc动作）
		 */
		ON_FIRST_TALK(false, "ON_FIRST_TALK"), // control the first dialog shown
		// by NPCs when they are clicked
		// (some quests must override
		// the default npc action)
		/**
		 * 从npc开始调用对话动作
		 */
		QUEST_START(true, "QUEST_START"), // onTalk action from start npcs
		/**
		 * 从npc所有的任务调用对话动作
		 */
		ON_TALK(true, "ON_TALK"), // onTalk action from npcs participating in a
		// quest
		/**
		 * 当怪物被某人攻击,触发攻击动作
		 */
		ON_ATTACK(true, "ON_ATTACK"), // onAttack action triggered when a mob
		// gets attacked by someone
		/**
		 * 当杀死怪物时,触发杀死动作
		 */
		ON_KILL(true, "ON_KILL"), // onKill action triggered when a mob gets
		// killed.
		/**
		 * npc产生,或重生时,触发产生动作
		 */
		ON_SPAWN(true, "ON_SPAWN"), // onSpawn action triggered when an NPC is
		// spawned or respawned.
		/**
		 * npc或怪物看到一个人使用技能（不管目标是什么,应该可以理解成玩家使用技能时怪物的反应）
		 */
		ON_SKILL_SEE(true, "ON_SKILL_SEE"), // NPC or Mob saw a person casting a
		// skill (regardless what the target
		// is).
		/**
		 * npc或怪物看到一个人使用技能（不管目标是什么,应该可以理解成玩家使用技能时怪物的反应）
		 */
		ON_FACTION_CALL(true, "ON_FACTION_CALL"), // NPC or Mob saw a person
		// casting a skill
		// (regardless what the
		// target is).
		/**
		 * 一个人进入npc或者怪物射程(攻击范围确认)
		 */
		ON_AGGRO_RANGE_ENTER(true, "ON_AGGRO_RANGE_ENTER"), // a person came
		// within the
		// Npc/Mob's range
		/**
		 * npc完成技能使用触发诅咒完成动作
		 */
		ON_SPELL_FINISHED(true, "ON_SPELL_FINISHED"), // on spell finished
		// action when npc
		// finish casting skill
		/**
		 * 从任务脚本控制学得技能对话
		 */
		ON_SKILL_LEARN(false, "ON_SKILL_LEARN"), // control the AcquireSkill
		// dialog from quest script
		/**
		 * 进入区域
		 */
		ON_ENTER_ZONE(true, "ON_ENTER_ZONE"), // on zone enter
		/**
		 * 离开区域
		 */
		ON_EXIT_ZONE(true, "ON_EXIT_ZONE"), // on zone exit
		/**
		 * 使用技能(包括物品使用)
		 */
		ON_USER_SKILL(true, "ON_USER_SKILL"),
		/**
		 * 玩家添加物品
		 */
		ON_USER_ADDITEM(true, "ON_USER_ADDITEM");
		/**
		 * 控制事件类型是否允许同一npc模板在多个任务 <br>
		 * 或者npc只能注册在最多一个指定事件的任务
		 */
		private boolean _allowMultipleRegistration;

		private String _name;

		QuestEventType(boolean allowMultipleRegistration, String name) {
			_allowMultipleRegistration = allowMultipleRegistration;
			_name = name;
		}

		public boolean isMultipleRegistrationAllowed() {
			return _allowMultipleRegistration;
		}

		public String getName() {
			return _name;
		}

		public static QuestEventType getQuestEventTypeByName(String name) {
			for (QuestEventType qet : QuestEventType.values()) {
				if (qet.getName().equals(name))
					return qet;
			}
			return null;
		}
	}

	/**
	 * Return ID of the quest
	 * 
	 * @return int
	 */
	public int getQuestIntId() {
		return _questId;
	}

	/**
	 * Add a new QuestState to the database and return it.<br>
	 * 根据任务模板添加一个新任务实例到数据库,并返回该对象
	 * 
	 * @param player
	 * @return QuestState : QuestState created
	 */
	public QuestState newQuestState(FuzePcInstance player) {
		QuestState qs = new QuestState(this, player, getInitialState());// 绑定一个新任务到玩家
		Quest.createQuestInDb(qs);
		return qs;
	}

	/**
	 * Return initial state of the quest
	 * 
	 * @return State
	 */
	public byte getInitialState() {
		return _initialState;
	}

	/**
	 * Return name of the quest
	 * 
	 * @return String
	 */
	public String getName() {
		return _name;
	}

	/**
	 * Return description of the quest
	 * 
	 * @return String
	 */
	public String getDescr() {
		return _descr;
	}

	/**
	 * Add a timer to the quest, if it doesn't exist already
	 * 
	 * @param name
	 *            : name of the timer (also passed back as "event" in onAdvEvent)
	 * @param time
	 *            : time in ms for when to fire the timer
	 * @param npc
	 *            : npc associated with this timer (can be null)
	 * @param player
	 *            : player associated with this timer (can be null)
	 */
	public void startQuestTimer(String name, int time, L2Npc npc, FuzePcInstance player) {
		startQuestTimer(name, time, npc, player, false);
	}

	/**
	 * Add a timer to the quest, if it doesn't exist already. If the timer is repeatable, it will auto-fire automatically, at a fixed rate, until explicitly canceled.
	 * 
	 * @param name
	 *            : name of the timer (also passed back as "event" in onAdvEvent)
	 * @param time
	 *            : time in ms for when to fire the timer
	 * @param npc
	 *            : npc associated with this timer (can be null)
	 * @param player
	 *            : player associated with this timer (can be null)
	 * @param repeatable
	 *            : indicates if the timer is repeatable or one-time.
	 */
	public void startQuestTimer(String name, int time, L2Npc npc, FuzePcInstance player, boolean repeating) {
		// Add quest timer if timer doesn't already exist
		FastList<QuestTimer> timers = getQuestTimers(name);
		// no timer exists with the same name, at all 
		if (timers == null) {
			timers = new FastList<QuestTimer>();
			timers.add(new QuestTimer(this, name, time, npc, player, repeating));
			_allEventTimers.put(name, timers);
		}
		// a timer with this name exists, but may not be for the same set of npc and player
		else {
			// if there exists a timer with this name, allow the timer only if the [npc, player] set is unique
			// nulls act as wildcards
			if (getQuestTimer(name, npc, player) == null) {
				try {
					_rwLock.writeLock().lock();
					timers.add(new QuestTimer(this, name, time, npc, player, repeating));
				} finally {
					_rwLock.writeLock().unlock();
				}
			}
		}
	}

	public QuestTimer getQuestTimer(String name, L2Npc npc, FuzePcInstance player) {
		FastList<QuestTimer> qt = getQuestTimers(name);

		if (qt == null || qt.isEmpty())
			return null;
		try {
			_rwLock.readLock().lock();
			for (QuestTimer timer : qt) {
				if (timer != null) {
					if (timer.isMatch(this, name, npc, player))
						return timer;
				}
			}

		} finally {
			_rwLock.readLock().unlock();
		}
		return null;
	}

	private FastList<QuestTimer> getQuestTimers(String name) {
		return _allEventTimers.get(name);
	}

	public void cancelQuestTimers(String name) {
		FastList<QuestTimer> timers = getQuestTimers(name);
		if (timers == null)
			return;
		try {
			_rwLock.writeLock().lock();
			for (QuestTimer timer : timers) {
				if (timer != null) {
					timer.cancel();
				}
			}
		} finally {
			_rwLock.writeLock().unlock();
		}
	}

	public void cancelQuestTimer(String name, L2Npc npc, FuzePcInstance player) {
		QuestTimer timer = getQuestTimer(name, npc, player);
		if (timer != null)
			timer.cancel();
	}

	public void removeQuestTimer(QuestTimer timer) {
		if (timer == null)
			return;
		FastList<QuestTimer> timers = getQuestTimers(timer.getName());
		if (timers == null)
			return;
		try {
			_rwLock.writeLock().lock();
			timers.remove(timer);
		} finally {
			_rwLock.writeLock().unlock();
		}
	}

	// 这些方法从Java调用
	/**
	 * 通知攻击
	 */
	public final boolean notifyAttack(L2Npc npc, FuzePcInstance attacker, int damage, boolean isPet, L2Skill skill) {
		String res = null;
		try {
			res = onAttack(npc, attacker, damage, isPet, skill);
		} catch (Exception e) {
			return showError(attacker, e);
		}
		return showResult(attacker, res);
	}

	/**
	 *通知死亡
	 */
	public final boolean notifyDeath(L2Character killer, L2Character victim, QuestState qs) {
		String res = null;
		try {
			res = onDeath(killer, victim, qs);
		} catch (Exception e) {
			return showError(qs.getPlayer(), e);
		}
		return showResult(qs.getPlayer(), res);
	}

	/**
	 * 通知中魔结束
	 */
	public final boolean notifySpellFinished(L2Npc instance, FuzePcInstance player, L2Skill skill) {
		String res = null;
		try {
			res = onSpellFinished(instance, player, skill);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);

	}

	/**
	 *通知产生
	 */
	public final boolean notifySpawn(L2Npc npc) {
		try {
			onSpawn(npc);
		} catch (Exception e) {
			_log.error("notify Spawn", Config.SERVER_ID, e);
			return true;
		}
		return false;
	}

	/**
	 *通知事件
	 */
	public final boolean notifyEvent(String event, L2Npc npc, FuzePcInstance player) {
		String res = null;
		try {
			res = onAdvEvent(event, npc, player);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);
	}

	/**
	 * 通知进入世界
	 */
	public final boolean notifyEnterWorld(FuzePcInstance player) {
		String res = null;
		try {
			res = onEnterWorld(player);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);
	}

	/**
	 * 通知杀死
	 */
	public final boolean notifyKill(L2Npc npc, FuzePcInstance killer, boolean isPet) {
		String res = null;
		try {
			res = onKill(npc, killer, isPet);
		} catch (Exception e) {
			return showError(killer, e);
		}
		return showResult(killer, res);
	}

	/**
	 * 通知对话
	 */
	public final boolean notifyTalk(L2Npc npc, QuestState qs) {
		String res = null;
		try {
			res = onTalk(npc, qs.getPlayer());
		} catch (Exception e) {
			return showError(qs.getPlayer(), e);
		}
		qs.getPlayer().setLastQuestNpcObject(npc.getObjectId());
		return showResult(qs.getPlayer(), res);
	}

	// 覆盖默认npc任务对话，这个定义为给定的npc
	/**
	 * 通知第一次对话
	 */
	public final boolean notifyFirstTalk(L2Npc npc, FuzePcInstance player) {
		String res = null;
		try {
			res = onFirstTalk(npc, player);
		} catch (Exception e) {
			return showError(player, e);
		}
		// if the quest returns text to display, display it.
		if (res != null && res.length() > 0)
			return showResult(player, res);
		// else tell the player that
		else
			player.sendPacket(ActionFailed.STATIC_PACKET);
		// note: if the default html for this npc needs to be shown, onFirstTalk should 
		// call npc.showChatWindow(player) and then return null.
		return true;
	}

	/**
	 * 通知获取技能列表
	 */
	public final boolean notifyAcquireSkillList(L2Npc npc, FuzePcInstance player) {
		String res = null;
		try {
			res = onAcquireSkillList(npc, player);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);
	}

	/**
	 * 通知获取技能信息
	 */
	public final boolean notifyAcquireSkillInfo(L2Npc npc, FuzePcInstance player, L2Skill skill) {
		String res = null;
		try {
			res = onAcquireSkillInfo(npc, player, skill);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);
	}

	/**
	 * 通知获取技能
	 */
	public final boolean notifyAcquireSkill(L2Npc npc, FuzePcInstance player, L2Skill skill) {
		String res = null;
		try {
			res = onAcquireSkill(npc, player, skill);
			if (res == "true")
				return true;
			else if (res == "false")
				return false;
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);
	}

	/**
	 *通知技能可视
	 */
	public final boolean notifySkillSee(L2Npc npc, FuzePcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet) {
		String res = null;
		try {
			res = onSkillSee(npc, caster, skill, targets, isPet);
		} catch (Exception e) {
			showError(caster, e);
		}
		showResult(caster, res);
		return true;
	}

	/**
	 *通知团体调用
	 */
	public final boolean notifyFactionCall(L2Npc npc, L2Npc caller, FuzePcInstance attacker, boolean isPet) {
		String res = null;
		try {
			res = onFactionCall(npc, caller, attacker, isPet);
		} catch (Exception e) {
			return showError(attacker, e);
		}
		return showResult(attacker, res);
	}

	/**
	 * 通知追击距离确认
	 */
	public final boolean notifyAggroRangeEnter(L2Npc npc, FuzePcInstance player, boolean isPet) {
		String res = null;
		try {
			res = onAggroRangeEnter(npc, player, isPet);
		} catch (Exception e) {
			showError(player, e);
		}
		showResult(player, res);
		return true;
	}

	/**
	 * 通知进入区域
	 */
	public final boolean notifyEnterZone(L2Character character, L2ZoneType zone) {
		FuzePcInstance player = character.getActingPlayer();
		String res = null;
		try {
			res = this.onEnterZone(character, zone);
		} catch (Exception e) {
			if (player != null)
				return showError(player, e);
		}
		if (player != null)
			return showResult(player, res);
		return true;
	}

	/**
	 * 通知退出区域
	 */
	public final boolean notifyExitZone(L2Character character, L2ZoneType zone) {
		FuzePcInstance player = character.getActingPlayer();
		String res = null;
		try {
			res = this.onExitZone(character, zone);
		} catch (Exception e) {
			if (player != null)
				return showError(player, e);
		}
		if (player != null)
			return showResult(player, res);
		return true;
	}

	/**
	 * 通知玩家使用技能
	 * 
	 * @param player
	 * @param skill
	 * @return 2010-4-27
	 * @author lyh
	 */
	public final boolean notifyUseSkill(FuzePcInstance player, L2Skill skill) {
		String res = null;
		try {
			res = onUseSkill(player, skill);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);

	}

	/**
	 * 得到玩家添加物品通知
	 * 
	 * @param player
	 * @param item
	 * @return 2010-5-11
	 * @author lyh
	 */
	public final boolean notifyAddItem(FuzePcInstance player, L2ItemInstance item) {
		String res = null;
		try {
			res = onAddItem(player, item);
		} catch (Exception e) {
			return showError(player, e);
		}
		return showResult(player, res);

	}

	// 这些都是Java调用方法调用脚本
	public String onAttack(L2Npc npc, FuzePcInstance attacker, int damage, boolean isPet) {
		return null;
	}

	public String onAttack(L2Npc npc, FuzePcInstance attacker, int damage, boolean isPet, L2Skill skill) {
		return onAttack(npc, attacker, damage, isPet);
	}

	public String onDeath(L2Character killer, L2Character victim, QuestState qs) {
		if (killer instanceof L2Npc)
			return onAdvEvent("", (L2Npc) killer, qs.getPlayer());
		else
			return onAdvEvent("", null, qs.getPlayer());
	}

	/**
	 * 前置事件(此方法一般在脚本中实现,此处一般被覆盖掉)
	 */
	public String onAdvEvent(String event, L2Npc npc, FuzePcInstance player) {
		// if not overridden by a subclass, then default to the returned value of the simpler (and older) onEvent override
		// if the player has a state, use it as parameter in the next call, else return null
		QuestState qs = player.getQuestState(getName());
		if (qs != null)
			return onEvent(event, qs);

		return null;
	}

	/**
	 * 事件逻辑
	 * 
	 * @param event
	 * @param qs
	 * @return
	 */
	public String onEvent(String event, QuestState qs) {
		return null;
	}

	/**
	 * 杀死怪物逻辑
	 * 
	 * @param npc
	 * @param killer
	 * @param isPet
	 * @return
	 */
	public String onKill(L2Npc npc, FuzePcInstance killer, boolean isPet) {
		return null;
	}

	/**
	 * 对话逻辑
	 * 
	 * @param npc
	 * @param talker
	 * @return
	 */
	public String onTalk(L2Npc npc, FuzePcInstance talker) {
		return null;
	}

	/**
	 * 第一次对话逻辑
	 * 
	 * @param npc
	 * @param player
	 * @return
	 */
	public String onFirstTalk(L2Npc npc, FuzePcInstance player) {
		return null;
	}

	/**
	 * 获取技能列表逻辑
	 * 
	 * @param npc
	 * @param player
	 * @return
	 */
	public String onAcquireSkillList(L2Npc npc, FuzePcInstance player) {
		return null;
	}

	/**
	 * 获取技能信息逻辑
	 * 
	 * @param npc
	 * @param player
	 * @param skill
	 * @return
	 */
	public String onAcquireSkillInfo(L2Npc npc, FuzePcInstance player, L2Skill skill) {
		return null;
	}

	/**
	 * 学得技能逻辑
	 * 
	 * @param npc
	 * @param player
	 * @param skill
	 * @return
	 */
	public String onAcquireSkill(L2Npc npc, FuzePcInstance player, L2Skill skill) {
		return null;
	}

	/**
	 * 技能反应逻辑
	 * 
	 * @param npc
	 * @param caster
	 * @param skill
	 * @param targets
	 * @param isPet
	 * @return
	 */
	public String onSkillSee(L2Npc npc, FuzePcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet) {
		return null;
	}

	/**
	 * 诅咒完成动作逻辑
	 * 
	 * @param npc
	 * @param player
	 * @param skill
	 * @return
	 */
	public String onSpellFinished(L2Npc npc, FuzePcInstance player, L2Skill skill) {
		return null;
	}

	/**
	 * 生产逻辑
	 * 
	 * @param npc
	 * @return
	 */
	public String onSpawn(L2Npc npc) {
		return null;
	}

	/**
	 * 派系呼叫逻辑
	 * 
	 * @param npc
	 * @param caller
	 * @param attacker
	 * @param isPet
	 * @return
	 */
	public String onFactionCall(L2Npc npc, L2Npc caller, FuzePcInstance attacker, boolean isPet) {
		return null;
	}

	/**
	 * 进入仇恨范围逻辑
	 * 
	 * @param npc
	 * @param player
	 * @param isPet
	 * @return
	 */
	public String onAggroRangeEnter(L2Npc npc, FuzePcInstance player, boolean isPet) {
		return null;
	}

	/**
	 * 进入世界逻辑
	 * 
	 * @param player
	 * @return 2010-3-25
	 * @author lyh
	 */
	public String onEnterWorld(FuzePcInstance player) {
		return null;
	}

	/**
	 * 进入区域逻辑
	 * 
	 * @param character
	 * @param zone
	 * @return
	 */
	public String onEnterZone(L2Character character, L2ZoneType zone) {
		return null;
	}

	/**
	 * 退出区域逻辑
	 * 
	 * @param character
	 * @param zone
	 * @return
	 */
	public String onExitZone(L2Character character, L2ZoneType zone) {
		return null;
	}

	/**
	 * 玩家使用技能逻辑
	 * 
	 * @param player
	 * @param skill
	 * @return 2010-4-27
	 * @author lyh
	 */
	public String onUseSkill(FuzePcInstance player, L2Skill skill) {
		return null;
	}

	/**
	 * 玩家添加物品逻辑
	 * 
	 * @param player
	 * @param item
	 * @return 2010-5-11
	 * @author lyh
	 */
	public String onAddItem(FuzePcInstance player, L2ItemInstance item) {
		return null;
	}

	/**
	 * Show message error to player who has an access level greater than 0
	 * 
	 * @param player
	 *            : L2PcInstance
	 * @param t
	 *            : Throwable
	 * @return boolean
	 */
	public boolean showError(FuzePcInstance player, Throwable t) {
		_log.error(this.getScriptName(), Config.SERVER_ID, t);
		if (player != null && player.getAccessLevel().isGm()) {
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			t.printStackTrace(pw);
			pw.close();
			String res = "<content>Script error " + sw.toString() + "</content><br>";
			return showResult(player, res);
		}
		return false;
	}

	/**
	 * Show a message to player.<BR>
	 * <BR>
	 * 显示结果给玩家<br>
	 * <U><I>Concept : </I></U><BR>
	 * 3 cases are managed according to the value of the parameter "res" :<BR>
	 * <LI><U>"res" ends with string ".html" :</U> an HTML is opened in order to be shown in a dialog box</LI> <LI><U>"res" starts with "<html>" :</U> the message hold in "res" is shown in a dialog box</LI> <LI><U>otherwise :</U> the message held in "res" is shown in chat box</LI>
	 * 
	 * @param qs
	 *            : QuestState
	 * @param res
	 *            : String pointing out the message to show at the player
	 * @return boolean
	 */
	public boolean showResult(FuzePcInstance player, String res) {
		if (res == null || res.isEmpty() || player == null)
			return true;
		if (res.endsWith(".xml")) {
			showXmlFile(player, res);
		} else if (res.startsWith("<form")) {
			NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
			npcReply.setHtml(res);
			npcReply.replace("%playername%", player.getName());
			
			if (player.getTarget() != null) {
				npcReply.replace("%objectId%", String.valueOf(player.getTarget().getObjectId()));
			} else {
				npcReply.replace("%objectId%", String.valueOf(player.getLastFolkNPC().getObjectId()));
			}
						
			player.sendPacket(npcReply);
			player.sendPacket(ActionFailed.STATIC_PACKET);
		} else if (res.startsWith("NPC")) {// 直接请求bypass,弹出npc对话,格式对照L2Npc.onBypassFeedback逻辑
			String[] strs = res.split(delimiter);
			String id = strs[1];
			
			L2Object object = FuzeWorld.getInstance().findObject(Long.parseLong(id));
			((L2Npc) object).onBypassFeedback(player, strs[2]);
		} else if (res.startsWith("ComQuest_")) {
			String[] strs = res.split("_");
			String id = strs[1];
			L2Object object = FuzeWorld.getInstance().findObject(Long.parseLong(id));
			((L2Npc) object).onBypassFeedback(player, "Quest");
		}else if (res.startsWith("DEFAULT_NPC")) {// 调用默认npc对话(常用对话配置)
			String[] strs = res.split(delimiter);
			String val = strs[1];
//			String filename = getHtmlPathint(5, Integer.parseInt(val));
			String filename = getShowResultHtml(val);
			NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
			npcReply.setFile(filename);
			npcReply.replace("%playername%", player.getName());
			player.sendPacket(npcReply);
			player.sendPacket(ActionFailed.STATIC_PACKET);
		} else if (res.startsWith("SYS_MSG")) {
			String[] strs = res.split(delimiter);
			String val = strs[1];
			player.sendMessage(val);
		} else {
			if (res != null && !"".equals(res.trim()) && !"accept".equalsIgnoreCase(res) && !"finish".equalsIgnoreCase(res) && !"timer_task".equalsIgnoreCase(res)) {
				player.sendMessage(res);
			} 
		}
		return false;
	}

	/**
	 * Add quests to the L2PCInstance of the player.<BR>
	 * <BR>
	 * 添加任务到玩家实例 <U><I>Action : </U></I><BR>
	 * Add state of quests, drops and variables for quests in the HashMap _quest of L2PcInstance <br>
	 * 添加不同的剧情任务，删除和改变剧情任务<br>
	 * (在l2源代码中EnterWorld.runImpl()调用了Quest.playerEnter( activeChar),当玩家进入世界用来加载玩家数据库中的任务)
	 * 
	 * @param player
	 *            : Player who is entering the world
	 */
	public final static void playerEnter(FuzePcInstance player) {

		/**
		 * CREATE TABLE IF NOT EXISTS `character_quests` ( <br>
		 * `charId` INT UNSIGNED NOT NULL DEFAULT 0,<br>
		 * `name` VARCHAR(40) NOT NULL DEFAULT '', <br>
		 * `var` VARCHAR(20) NOT NULL DEFAULT '', <br>
		 * `value` VARCHAR(255) ,<br>
		 * `class_index` int(1) NOT NULL default '0', PRIMARY KEY (`charId`,`name`,`var`,`class_index`) );
		 */
		// PreparedStatement invalidQuestData =
		// con.prepareStatement("DELETE FROM character_quests WHERE charId=? and name=?");
		// PreparedStatement invalidQuestDataVar =
		// con.prepareStatement("delete FROM character_quests WHERE charId=? and name=? and var=?");
		// statement =
		// con.prepareStatement("SELECT name,value FROM character_quests WHERE charId=? AND var=?");
		// statement =
		// con.prepareStatement("SELECT name,var,value FROM character_quests WHERE charId=? AND var<>?");

		Iterator<FuzePcInstance.PlayerQuestValues> ite = player.getQuestValues().iterator();

		while (ite.hasNext()) {
			FuzePcInstance.PlayerQuestValues pqv = ite.next();
			String val = pqv.getVal();
			String questId = pqv.getName();
			if (val.equals("<state>")) {// 根据任务状态填充QuestState
				// Get ID of the quest and ID of its state
				// 获取任务id和状态

				String statename = pqv.getValue();
				// Search quest associated with the ID
				// 搜索和id关联的任务
				Quest q = QuestManager.getInstance().getQuest(questId);
				if (q == null) {
					_log.warn("Unknown quest " + questId + " for player " + player.getName(), Config.SERVER_ID, "任务");
					if (Config.AUTODELETE_INVALID_QUEST_DATA) {// 自动删除无效任务数据
						ite.remove();
					}
					continue;
				}
				// Create a new QuestState for the player that will be added to
				// the player's list of quests
				// 创建一个新的QuestState,添加到玩家用于剧情任务列表
				new QuestState(q, player, State.getStateId(statename));
			} else if (val.equals("<events>")) {// 注册在玩家的身上事件
				String eventName = pqv.getValue();
				Quest q = QuestManager.getInstance().getQuest(questId);
				player.addQuestEvent(QuestEventType.getQuestEventTypeByName(eventName), q);
			} else {// 除开任务状态,其他任务数据
				String value = pqv.getValue();
				// Get the QuestState saved in the loop before
				// 在循环前获取已保存的QuestState
				QuestState qs = player.getQuestState(questId);
				if (qs == null) {
					_log.warn("Lost variable " + val + " in quest " + questId + " for player " + player.getName(), Config.SERVER_ID, "任务");
					if (Config.AUTODELETE_INVALID_QUEST_DATA) {// 自动删除无效任务数据
						ite.remove();
					}
					continue;
				}
				// Add parameter to the quest
				// 添加参数到任务中
				qs.setInternal(val, value);
			}
		}

		// events 事件
		for (String name : _allEventsS.keySet()) {
			player.processQuestEvent(name, "enter");
		}
	}

	/**
	 * 保存任务全局数据 <br>
	 * Insert (or Update) in the database variables that need to stay persistant for this quest after a reboot. This function is for storage of values that do not related to a specific player but are global for all characters. For example, if we need to disable a quest-gatekeeper until a certain time (as is done with some grand-boss gatekeepers), we can save that time in the DB. <br>
	 * 在重启后,任需要持续保存的插入或更新的数据库的任务数据<br>
	 * 此方法是为不与特定的玩家相关<br>
	 * 全局的所有人物。例如，如果我们需要取消守护者任务 在某个时间（如完成大的boss守卫），我们可以保存那时间些在DB中。
	 * 
	 * @param var
	 *            : String designating the name of the variable for the quest 变量名称
	 * @param value
	 *            : String designating the value of the variable for the quest 变量值
	 */
	public final void saveGlobalQuestVar(String var, String value) {
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement;
			statement = con.prepareStatement("REPLACE INTO quest_global_data (quest_name,var,value) VALUES (?,?,?)");
			statement.setString(1, getName());
			statement.setString(2, var);
			statement.setString(3, value);
			statement.executeUpdate();
			statement.close();
		} catch (Exception e) {
			_log.error("could not insert global quest variable:", Config.SERVER_ID, e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * Read from the database a previously saved variable for this quest. Due to performance considerations, this function should best be used only when the quest is first loaded. Subclasses of this class can define structures into which these loaded values can be saved. However, on-demand usage of this function throughout the script is not prohibited, only not recommended. Values read from this function were entered by calls to "saveGlobalQuestVar"
	 * 
	 * @param var
	 *            : String designating the name of the variable for the quest
	 * @return String : String representing the loaded value for the passed var, or an empty string if the var was invalid
	 */
	public final String loadGlobalQuestVar(String var) {
		String result = "";
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement;
			statement = con.prepareStatement("SELECT value FROM quest_global_data WHERE quest_name = ? AND var = ?");
			statement.setString(1, getName());
			statement.setString(2, var);
			ResultSet rs = statement.executeQuery();
			if (rs.first())
				result = rs.getString(1);
			rs.close();
			statement.close();
		} catch (Exception e) {
			_log.error("could not load global quest variable:", Config.SERVER_ID, e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
		return result;
	}

	/**
	 * Permanently delete from the database a global quest variable that was previously saved for this quest.
	 * 
	 * @param var
	 *            : String designating the name of the variable for the quest
	 */
	public final void deleteGlobalQuestVar(String var) {
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement;
			statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ? AND var = ?");
			statement.setString(1, getName());
			statement.setString(2, var);
			statement.executeUpdate();
			statement.close();
		} catch (Exception e) {
			_log.error("could not delete global quest variable:", Config.SERVER_ID, e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * Permanently delete from the database all global quest variables that was previously saved for this quest.
	 */
	public final void deleteAllGlobalQuestVars() {
		Connection con = null;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement;
			statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ?");
			statement.setString(1, getName());
			statement.executeUpdate();
			statement.close();
		} catch (Exception e) {
			_log.error("could not delete global quest variables:", Config.SERVER_ID, e);
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 在数据库中插入玩家的任务。db_table[character_quests]INSERT
	 * 
	 * @param qs
	 *            : QuestState pointing out the state of the quest
	 * @param var
	 *            : 变量名
	 * @param value
	 *            : 变量值
	 */
	public static void createQuestVarInDb(QuestState qs, String var, String value) {
		// con.prepareStatement("INSERT INTO character_quests (charId,name,var,value) VALUES (?,?,?,?)");
		PlayerQuestValues pqv = qs.getPlayer().newPlayerQuestValues(qs.getQuestName(), var, value);
		qs.getPlayer().getQuestValues().add(pqv);
	}

	/**
	 * 更新任务对应变量的值,db_table[character_quests]INSERT<br>
	 * <U><I>Actions :</I></U><BR>
	 * The selection of the right record is made with : <LI>charId = qs.getPlayer().getObjectID()</LI> <LI>name = qs.getQuest().getName()</LI> <LI>var = var</LI> <BR>
	 * <BR>
	 * The modification made is : <LI>value = parameter value</LI>
	 * 
	 * @param qs
	 *            : Quest State
	 * @param var
	 *            : 变量名
	 * @param value
	 *            : 变量值
	 */
	public static void updateQuestVarInDb(QuestState qs, String var, String value) {
		// con.prepareStatement("UPDATE character_quests SET value=? WHERE charId=? AND name=? AND var = ?");
		String questName = qs.getQuestName();
		List<FuzePcInstance.PlayerQuestValues> list = qs.getPlayer().getQuestValues();
		for (FuzePcInstance.PlayerQuestValues pqv : list) {
			if (pqv.getName().equals(questName) && pqv.getVal().equals(var)) {
				pqv.setValue(value);
			}
		}
	}

	/**
	 * 删除了玩家的任务变量<br>
	 * Delete a variable of player's quest from the database.
	 * 
	 * @param qs
	 *            : object QuestState pointing out the player's quest
	 * @param var
	 *            : String designating the variable characterizing the quest
	 */
	public static void deleteQuestVarInDb(QuestState qs, String var) {
		// statement =
		// con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=? AND var=?");
		String questName = qs.getQuestName();
		List<FuzePcInstance.PlayerQuestValues> list = qs.getPlayer().getQuestValues();
		for (Iterator<FuzePcInstance.PlayerQuestValues> ite = list.iterator(); ite.hasNext();) {
			FuzePcInstance.PlayerQuestValues pqv = ite.next();
			if (pqv.getName().equals(questName) && pqv.getVal().equals(var))
				ite.remove();
		}
	}

	/**
	 * 删除玩家的任务。<br>
	 * Delete the player's quest from database.
	 * 
	 * @param qs
	 *            : QuestState pointing out the player's quest
	 */
	public static void deleteQuestInDb(QuestState qs) {
		// statement =
		// con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=?");
		String questName = qs.getQuestName();
		List<FuzePcInstance.PlayerQuestValues> list = qs.getPlayer().getQuestValues();
		for (Iterator<FuzePcInstance.PlayerQuestValues> ite = list.iterator(); ite.hasNext();) {
			FuzePcInstance.PlayerQuestValues pqv = ite.next();
			if (pqv.getName().equals(questName))
				ite.remove();
		}
	}

	/**
	 * Create a record in database for quest.<BR>
	 * <BR>
	 * 创建一个任务记录到数据库<br>
	 * <U><I>Actions :</I></U><BR>
	 * Use fucntion createQuestVarInDb() with following parameters :<BR>
	 * <LI>QuestState : parameter sq that puts in fields of database :
	 * <UL type="square">
	 * <LI>charId : ID of the player</LI>
	 * <LI>name : name of the quest</LI>
	 * </UL>
	 * </LI> <LI>var : string "&lt;state&gt;" as the name of the variable for the quest</LI> <LI>val : string corresponding at the ID of the state (in fact, initial state)</LI>
	 * 
	 * @param qs
	 *            : QuestState
	 */
	public static void createQuestInDb(QuestState qs) {
		createQuestVarInDb(qs, "<state>", State.getStateName(qs.getState()));
	}

	/**
	 * Update informations regarding quest in database.<BR>
	 * <U><I>Actions :</I></U><BR>
	 * <LI>Get ID state of the quest recorded in object qs</LI> <LI>Test if quest is completed. If true, add a star (*) before the ID state</LI> <LI>Save in database the ID state (with or without the star) for the variable called "&lt;state&gt;" of the quest</LI>
	 * 
	 * @param qs
	 *            : QuestState
	 */
	public static void updateQuestInDb(QuestState qs) {
		String val = State.getStateName(qs.getState());
		updateQuestVarInDb(qs, "<state>", val);
	}

	/**
	 * Add this quest to the list of quests that the passed mob will respond to for the specified Event type.<BR>
	 * <BR>
	 * 添加任务通过剧情任务列表，通过怪物将响应指定的事件类型。
	 * 
	 * @param npcId
	 *            : id of the NPC to register
	 * @param eventType
	 *            : type of event being registered
	 * @return L2NpcTemplate : Npc Template corresponding to the npcId, or null if the id is invalid
	 */
	public NpcTemplate addEventId(int npcId, QuestEventType eventType) {
		try {
			NpcTemplate t = NpcTemplateTable.getInstance().getTemplate(npcId);
			if (t != null) {
				t.addQuestEvent(eventType, this);
			}
			return t;
		} catch (Exception e) {
			System.out.println("Error Quest:" + this.getName());
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 添加任务通过剧情任务列表，通过玩家将响应指定的事件类型。
	 * 
	 * @param playerId
	 *            玩家Id
	 * @param eventType
	 * @return 2010-4-27
	 * @author lyh
	 */
	public FuzePcInstance addEventIdOnPlayer(long playerId, QuestEventType eventType) {
		try {
			FuzePcInstance player = FuzeWorld.getInstance().getPlayer(playerId);
			if (player != null) {
				player.addQuestEvent(eventType, this);
			}
			return player;
		} catch (Exception e) {
			System.out.println("Error Quest:" + this.getName());
			e.printStackTrace();
			return null;
		}
	}

	// 脚本调用接口注册事件
	/**
	 * Add the quest to the NPC's startQuest<br>
	 * 添加任务到开始的npc
	 * 
	 * @param npcId
	 * @return L2NpcTemplate : Start NPC
	 */
	public NpcTemplate addStartNpc(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.QUEST_START);
	}

	/**
	 * Add the quest to the NPC's first-talk (default action dialog)<br>
	 * 添加任务第一次对话到npc(默认操作对话框)
	 * 
	 * @param npcId
	 * @return L2NpcTemplate : Start NPC
	 */
	public NpcTemplate addFirstTalkId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_FIRST_TALK);
	}

	/**
	 * Add the NPC to the AcquireSkill dialog <br>
	 * 添加npc学得技能对话框
	 * 
	 * @param npcId
	 * @return L2NpcTemplate : NPC
	 */
	public NpcTemplate addAcquireSkillId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_SKILL_LEARN);
	}

	/**
	 * Add this quest to the list of quests that the passed mob will respond to for Attack Events.<BR>
	 * <BR>
	 * 添加此任务到任务列表，怪物将响应的攻击事件
	 * 
	 * @param attackId
	 * @return int : attackId
	 */
	public NpcTemplate addAttackId(int attackId) {
		return addEventId(attackId, Quest.QuestEventType.ON_ATTACK);
	}

	/**
	 * Add this quest to the list of quests that the passed mob will respond to for Kill Events.<BR>
	 * <BR>
	 * 添加此任务到任务列表，怪物将响应的杀事件
	 * 
	 * @param killId
	 * @return int : killId
	 */
	public NpcTemplate addKillId(int killId) {
		return addEventId(killId, Quest.QuestEventType.ON_KILL);
	}

	/**
	 * Add this quest to the list of quests that the passed npc will respond to for Talk Events.<BR>
	 * <BR>
	 * 添加此任务到任务列表，npc将响应对话事件
	 * 
	 * @param talkId
	 *            : ID of the NPC
	 * @return int : ID of the NPC
	 */
	public NpcTemplate addTalkId(int talkId) {
		return addEventId(talkId, Quest.QuestEventType.ON_TALK);
	}

	/**
	 * Add this quest to the list of quests that the passed npc will respond to for Spawn Events.<BR>
	 * <BR>
	 * 添加此任务的任务列表，通过npc作出反应的生产事件
	 * 
	 * @param talkId
	 *            : ID of the NPC
	 * @return int : ID of the NPC
	 */
	public NpcTemplate addSpawnId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_SPAWN);
	}

	/**
	 * Add this quest to the list of quests that the passed npc will respond to for Skill-See Events.<BR>
	 * <BR>
	 * 添加此任务的任务列表，通过npc作出反应的技能释放事件
	 * 
	 * @param talkId
	 *            : ID of the NPC
	 * @return int : ID of the NPC
	 */
	public NpcTemplate addSkillSeeId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_SKILL_SEE);
	}

	public NpcTemplate addSpellFinishedId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_SPELL_FINISHED);
	}

	/**
	 * Add this quest to the list of quests that the passed npc will respond to for Faction Call Events.<BR>
	 * <BR>
	 * 
	 * @param talkId
	 *            : ID of the NPC
	 * @return int : ID of the NPC
	 */
	public NpcTemplate addFactionCallId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_FACTION_CALL);
	}

	/**
	 * Add this quest to the list of quests that the passed npc will respond to for Character See Events.<BR>
	 * <BR>
	 * 
	 * @param talkId
	 *            : ID of the NPC
	 * @return int : ID of the NPC
	 */
	public NpcTemplate addAggroRangeEnterId(int npcId) {
		return addEventId(npcId, Quest.QuestEventType.ON_AGGRO_RANGE_ENTER);
	}

	public L2ZoneType addEnterZoneId(int zoneId) {
		//		try {
		//			L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
		//			if (zone != null) {
		//				zone.addQuestEvent(Quest.QuestEventType.ON_ENTER_ZONE, this);
		//			}
		//			return zone;
		//		} catch (Exception e) {
		//			e.printStackTrace();
		//			return null;
		//		}
		return null;
	}

	public L2ZoneType addExitZoneId(int zoneId) {
		//		try {
		//			L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
		//			if (zone != null) {
		//				zone.addQuestEvent(Quest.QuestEventType.ON_EXIT_ZONE, this);
		//			}
		//			return zone;
		//		} catch (Exception e) {
		//			e.printStackTrace();
		//			return null;
		//		}
		return null;
	}

	/**
	 * 使用技能(包括物品使用)
	 * 
	 * @param playerId
	 * @return 2010-4-27
	 * @author lyh
	 */
	public FuzePcInstance addUserSkill(long playerId) {
		return addEventIdOnPlayer(playerId, Quest.QuestEventType.ON_USER_SKILL);
	}

	/**
	 * 玩家获得物品
	 * 
	 * @param playerId
	 * @return 2010-5-12
	 * @author lyh
	 */
	public FuzePcInstance addUserAddItme(long playerId) {
		return addEventIdOnPlayer(playerId, Quest.QuestEventType.ON_USER_ADDITEM);
	}

	// returns a random party member's L2PcInstance for the passed player's party
	// returns the passed player if he has no party.
	public FuzePcInstance getRandomPartyMember(FuzePcInstance player) {
		// NPE prevention.  If the player is null, there is nothing to return
		if (player == null)
			return null;
		if ((player.getParty() == null) || (player.getParty().getPartyMembers().isEmpty()))
			return player;
		L2Party party = player.getParty();
		return party.getPartyMembers().get(Rnd.get(party.getPartyMembers().size()));
	}

	/**
	 * Auxilary function for party quests. Note: This function is only here because of how commonly it may be used by quest developers. For any variations on this function, the quest script can always handle things on its own
	 * 
	 * @param player
	 *            : the instance of a player whose party is to be searched
	 * @param value
	 *            : the value of the "cond" variable that must be matched
	 * @return L2PcInstance: L2PcInstance for a random party member that matches the specified condition, or null if no match.
	 */
	public FuzePcInstance getRandomPartyMember(FuzePcInstance player, String value) {
		return getRandomPartyMember(player, "cond", value);
	}

	/**
	 * Auxilary function for party quests. Note: This function is only here because of how commonly it may be used by quest developers. For any variations on this function, the quest script can always handle things on its own
	 * 
	 * @param player
	 *            : the instance of a player whose party is to be searched
	 * @param var
	 *            /value: a tuple specifying a quest condition that must be satisfied for a party member to be considered.
	 * @return L2PcInstance: L2PcInstance for a random party member that matches the specified condition, or null if no match. If the var is null, any random party member is returned (i.e. no condition is applied). The party member must be within 1500 distance from the target of the reference player, or if no target exists, 1500 distance from the player itself.
	 */
	public FuzePcInstance getRandomPartyMember(FuzePcInstance player, String var, String value) {
		// if no valid player instance is passed, there is nothing to check...
		if (player == null)
			return null;

		// for null var condition, return any random party member.
		if (var == null)
			return getRandomPartyMember(player);

		// normal cases...if the player is not in a party, check the player's state
		QuestState temp = null;
		L2Party party = player.getParty();
		// if this player is not in a party, just check if this player instance matches the conditions itself
		if ((party == null) || (party.getPartyMembers().isEmpty())) {
			temp = player.getQuestState(getName());
			if ((temp != null) && (temp.get(var) != null) && ((String) temp.get(var)).equalsIgnoreCase(value))
				return player; // match

			return null; // no match
		}

		// if the player is in a party, gather a list of all matching party members (possibly 
		// including this player) 
		FastList<FuzePcInstance> candidates = new FastList<FuzePcInstance>();

		// get the target for enforcing distance limitations.
		L2Object target = player.getTarget();
		if (target == null)
			target = player;

		for (FuzePcInstance partyMember : party.getPartyMembers()) {
			if (partyMember == null)
				continue;
			temp = partyMember.getQuestState(getName());
			if ((temp != null) && (temp.get(var) != null) && ((String) temp.get(var)).equalsIgnoreCase(value) && partyMember.isInsideRadius(target, 150))
				candidates.add(partyMember);
		}
		// if there was no match, return null...
		if (candidates.isEmpty())
			return null;

		// if a match was found from the party, return one of them at random.
		return candidates.get(Rnd.get(candidates.size()));
	}

	/**
	 * Auxilary function for party quests. Note: This function is only here because of how commonly it may be used by quest developers. For any variations on this function, the quest script can always handle things on its own
	 * 
	 * @param player
	 *            : the instance of a player whose party is to be searched
	 * @param state
	 *            : the state in which the party member's queststate must be in order to be considered.
	 * @return L2PcInstance: L2PcInstance for a random party member that matches the specified condition, or null if no match. If the var is null, any random party member is returned (i.e. no condition is applied).
	 */
	public FuzePcInstance getRandomPartyMemberState(FuzePcInstance player, byte state) {
		// if no valid player instance is passed, there is nothing to check...
		if (player == null)
			return null;

		// normal cases...if the player is not in a partym check the player's state
		QuestState temp = null;
		L2Party party = player.getParty();
		// if this player is not in a party, just check if this player instance matches the conditions itself
		if ((party == null) || (party.getPartyMembers().isEmpty())) {
			temp = player.getQuestState(getName());
			if ((temp != null) && (temp.getState() == state))
				return player; // match

			return null; // no match
		}

		// if the player is in a party, gather a list of all matching party members (possibly 
		// including this player) 
		FastList<FuzePcInstance> candidates = new FastList<FuzePcInstance>();

		// get the target for enforcing distance limitations.
		L2Object target = player.getTarget();
		if (target == null)
			target = player;

		for (FuzePcInstance partyMember : party.getPartyMembers()) {
			if (partyMember == null)
				continue;
			temp = partyMember.getQuestState(getName());
			if ((temp != null) && (temp.getState() == state) && partyMember.isInsideRadius(target, 150))
				candidates.add(partyMember);
		}
		// if there was no match, return null...
		if (candidates.isEmpty())
			return null;

		// if a match was found from the party, return one of them at random.
		return candidates.get(Rnd.get(candidates.size()));
	}

	/**
	 * Show HTML file to client 显示html文件到客户端
	 * 
	 * @param fileName
	 * @return String : message sent to client
	 */
	public String showXmlFile(FuzePcInstance player, String fileName) {
		String questName = getName();
		int questId = getQuestIntId();
		//Create handler to file linked to the quest
		String directory = getDescr().toLowerCase();
		String content = HtmCache.getInstance().getHtm("data/scripts/" + directory + "/" + questName + "/" + fileName);

		if (content == null)
			content = HtmCache.getInstance().getHtmForce("data/scripts/quests/" + questName + "/" + fileName);

		if (player != null && player.getTarget() != null)
			content = content.replaceAll("%objectId%", String.valueOf(player.getTarget().getObjectId()));

		//Send message to client if message not empty     
		if (content != null) {
			// if (questId > 0 && questId < 20000) {
			// // NpcQuestHtmlMessage npcReply = new
			// // NpcQuestHtmlMessage(5,questId);
			// // npcReply.setHtml(content);
			// // npcReply.replace("%playername%", player.getName());
			// // player.sendPacket(npcReply);
			// } else {
			NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
			npcReply.setHtml(content);
			npcReply.replace("%playername%", player.getName());
			player.sendPacket(npcReply);
			// }
		}

		return content;
	}

	// =========================================================
	//  QUEST SPAWNS
	// =========================================================

	public class DeSpawnScheduleTimerTask implements Runnable {
		L2Npc _npc = null;

		public DeSpawnScheduleTimerTask(L2Npc npc) {
			_npc = npc;
		}

		public void run() {
			_npc.onDecay();
		}
	}

	// Method - Public
	/**
	 * 添加临时(任务)生成 返回新生成npc实例<br>
	 * Add a temporary (quest) spawn<br>
	 * Return instance of newly spawned npc
	 */
	public L2Npc addSpawn(int npcId, L2Character cha) {
		return addSpawn(npcId, cha.getX(), cha.getY(), false, 0, false);
	}

	/**
	 * 添加临时(任务)生成 返回新生成npc实例
	 * 
	 * Add a temporary (quest) spawn <br>
	 * Return instance of newly spawned npc <br>
	 * with summon animation
	 */
	public L2Npc addSpawn(int npcId, L2Character cha, boolean isSummonSpawn) {
		return addSpawn(npcId, cha.getX(), cha.getY(), false, 0, isSummonSpawn);
	}

	public L2Npc addSpawn(int npcId, int x, int y, boolean randomOffSet, int despawnDelay) {
		return addSpawn(npcId, x, y, randomOffSet, despawnDelay, false);
	}

	public L2Npc addSpawn(int npcId, int x, int y, boolean randomOffset, int despawnDelay, boolean isSummonSpawn) {
		return addSpawn(npcId, x, y, randomOffset, despawnDelay, isSummonSpawn, 0);
	}

	public L2Npc addSpawn(int npcId, int x, int y, boolean randomOffset, int despawnDelay, boolean isSummonSpawn, int instanceId) {
		L2Npc result = null;
		try {
			NpcTemplate template = NpcTemplateTable.getInstance().getTemplate(npcId);
			if (template != null) {
				// Sometimes, even if the quest script specifies some xyz (for example npc.getX() etc) by the time the code
				// reaches here, xyz have become 0!  Also, a questdev might have purposely set xy to 0,0...however,
				// the spawn code is coded such that if x=y=0, it looks into location for the spawn loc!  This will NOT work
				// with quest spawns!  For both of the above cases, we need a fail-safe spawn.  For this, we use the 
				// default spawn location, which is at the player's loc.
				if ((x == 0) && (y == 0)) {
					_log.warn("Failed to adjust bad locks for quest spawn!  Spawn aborted!", Config.SERVER_ID, "任务");
					return null;
				}
				if (randomOffset) {
					int offset;

					offset = Rnd.get(2); // Get the direction of the offset
					if (offset == 0) {
						offset = -1;
					} // make offset negative
					offset *= Rnd.get(50, 100);
					x += offset;

					offset = Rnd.get(2); // Get the direction of the offset
					if (offset == 0) {
						offset = -1;
					} // make offset negative
					offset *= Rnd.get(50, 100);
					y += offset;
				}
				L2Spawn spawn = new L2Spawn(template);
				spawn.setInstanceId(instanceId);

				spawn.setLocx(x);
				spawn.setLocy(y);

				spawn.stopRespawn();
				result = spawn.doSpawn();

				if (despawnDelay > 0)
					new Timer(despawnDelay, false, new DeSpawnScheduleTimerTask(result)).start();

				return result;
			}
		} catch (Exception e1) {
			_log.error("Could not spawn Npc " + npcId, Config.SERVER_ID, e1);
		}

		return null;
	}

	public int[] getRegisteredItemIds() {
		return questItemIds;
	}

	/**
	 * @see labox.innovation.gameserver.scripting.ManagedScript#getScriptName()
	 */
	@Override
	public String getScriptName() {
		return this.getName();
	}

	/**
	 * @see labox.innovation.gameserver.scripting.ManagedScript#setActive(boolean)
	 */
	@Override
	public void setActive(boolean status) {
		// TODO implement me
	}

	/**
	 * @see labox.innovation.gameserver.scripting.ManagedScript#reload()
	 */
	@Override
	public boolean reload() {
		unload();
		return super.reload();
	}

	/**
	 * @see labox.innovation.gameserver.scripting.ManagedScript#unload()
	 */
	@Override
	public boolean unload() {
		return unload(true);
	}

	public boolean unload(boolean removeFromList) {
		this.saveGlobalData();
		// cancel all pending timers before reloading.
		// if timers ought to be restarted, the quest can take care of it
		// with its code (example: save global data indicating what timer must 
		// be restarted).
		for (FastList<QuestTimer> timers : _allEventTimers.values())
			for (QuestTimer timer : timers)
				timer.cancel();
		_allEventTimers.clear();
		if (removeFromList)
			return QuestManager.getInstance().removeQuest(this);
		else
			return true;
	}

	/**
	 * @see labox.innovation.gameserver.scripting.ManagedScript#getScriptManager()
	 */
	@Override
	public ScriptManager<?> getScriptManager() {
		return QuestManager.getInstance();
	}

	public void setOnEnterWorld(boolean val) {
		_onEnterWorld = val;
	}

	public boolean getOnEnterWorld() {
		return _onEnterWorld;
	}

	/**
	 * 初始化任务
	 * 
	 * 2010-4-19
	 * 
	 * @author lyh
	 */
	public void initQuest() {

	}

	public ILogicTemplet getLogicTemplet() {
		return null;
	}

	/**
	 * 获取默认npc对话
	 * 
	 * @param npcId
	 * @param val
	 * @return 2010-4-30
	 * @author lyh
	 */
	public static final String getHtmlPathint(int npcId, int val) {
		String pom = "";

		if (val == 0)
			pom = "" + npcId;
		else
			pom = npcId + "-" + val;

		String temp = "data/xml/default/" + pom + ".xml";

		if (!Config.LAZY_CACHE) {
			// If not running lazy cache the file must be in the cache or it doesnt exist
			// 如果没有运行延迟缓存,该文件必须在缓存或不存在
			if (HtmCache.getInstance().contains(temp))
				return temp;
		} else {
			if (HtmCache.getInstance().isLoadable(temp))
				return temp;
		}

		// If the file is not found, the standard message "I have nothing to say to you" is returned
		return "/data/xml/npcdefault.xml";
	}
	
	public static final String getShowResultHtml(String val) {
		return "/data/xml/npcdefault_" +  val + ".xml";
	}
}
